package com.scott.itcast.thread.lesson12;

import java.util.Random;

/**
 * 线程范围内的变量
 * 线程范围内的共享数据
 * 各自线程上的数据应该是独立的
 * 在线程范围内不管哪个模块去拿数据。拿到的都是同1份数据
 * 每个数据库connection都是一个独立的线程
 * 2个事务之间的connection不能相同
 * 2个线程之间不影响对象的数据和事务  
 * 这件事务在线程范围内搞定,不要去影响别的线程的事务 。
 * connection在线程内共享,在线程外独立
 */
public class ThreadLocalTest {

	static ThreadLocal<Integer> tl= new ThreadLocal<Integer>();
	static ThreadLocal<ThreadScopeEntity> myData= new ThreadLocal<ThreadScopeEntity>();
	
	public static void main(String[] args) {
		
		for(int i=0; i<2;i++){
			//new2个线程
			new Thread(new Runnable(){
				public void run() {
					int data =new Random().nextInt();
					System.out.println(Thread.currentThread().getName() +"准备好data的值为："+data);
					
					//拿到与本线程相关的实例
					ThreadScopeEntity.getThreadInstance().setAge(data);
					ThreadScopeEntity.getThreadInstance().setName("huanghoulin"+data);
					
					//把数据存到当前线程中
					tl.set(data);
					
					//A模块去取数据
					new A().get();
					
					//B模块去取数据
					new B().get();
				}
				
			}).start();
		}
		
		
		
	}
	
	//A模块
	static class A{
		public void get(){
			int data =tl.get();
			
			//取到与当前线程相关的实例对象
			ThreadScopeEntity entity = ThreadScopeEntity.getThreadInstance();
			System.out.println("A模块获取线程 "+Thread.currentThread().getName() +"准备好的data的值为："+data);
			System.out.println("A模块获取线程 "+Thread.currentThread().getName() +"准备好的data的值为："+data +" entity为："+entity);
		}
	}
	
	//B模块
	static class B{
		public void get(){
			int data =tl.get();
			
			//取到与当前线程相关的实例对象
			ThreadScopeEntity entity = ThreadScopeEntity.getThreadInstance();
			System.out.println("B模块获取线程 "+Thread.currentThread().getName() +"准备好的data的值为："+data);
			System.out.println("B模块获取线程 "+Thread.currentThread().getName() +"准备好的data的值为："+data +" entity为："+entity);
		}
	}

}


/**
 * 设计 ：  线程范围内共享的对象
 * 我这个对象的实例是与每个线程相关的。那么这是设计就交给我这个类自己。其他用户在任意线程上调用我的方法getThreadInstance 得到的自然是与线程相关的实例。
 * 这个类是与线程绑定的。
 * 2个线程,各自有各自的对象。彼此之间互不影响。
 * 在这里  new了2个对象 并且把对象分别绑定不同的线程。  
 * 
 * 在struts2中。每1个请求request都是1个线程。
 * 每个请求都会绑定1个线程。 会把与这个请求相关的所有数据都装到1个容器中。而这个容器中装的东西只对这个请求有效。如果换另外1个请求,就是另外1个容器。
 * 
 * 虚拟机死的时候通知我:调用Runtime的addShutdownHook()方法：比如可以在这个线程中写个方法：当虚拟机死了的时候给我发邮件。
 * Runtime public void addShutdownHook(Thread hook)
 * 
 * 
 */
class ThreadScopeEntity{
	
	private static ThreadLocal<ThreadScopeEntity> map = new ThreadLocal<ThreadScopeEntity>();
	private ThreadScopeEntity(){
		
	}
	
	public static ThreadScopeEntity getThreadInstance(){
		ThreadScopeEntity instance = map.get();
		if(null == instance){
			System.out.println("ThreadScopeEntity被实例化了");
			instance = new ThreadScopeEntity();
			map.set(instance);
		}
		
		return instance;
	}
	
	private String name;
	private int    age;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	@Override
	public String toString() {
		return "MyThreadScopeData [name=" + name + ", age=" + age + "]";
	}
	
}


